# -*- encoding: utf-8 -*-
"""
@File    : singleton.py
@Author  : lilong
@Time    : 2021/12/18 5:27 下午
"""

"""
链接：https://www.jianshu.com/p/6a1690f0dd00

单例模式确保某一个类只有一个实例，而且自行实例化并向整个系统提供这个实例单例模式。
单例模式只应在有真正的“单一实例”的需求时才可使用。

单例模式就是确保一个类只有一个实例.当你希望整个系统中,某个类只有一个实例时,单例模式就派上了用场。

比如,某个服务器的配置信息存在在一个文件中,客户端通过AppConfig类来读取配置文件的信息.如果程序的运行的过程中,
很多地方都会用到配置文件信息,则就需要创建很多的AppConfig实例,这样就导致内存中有很多AppConfig对象的实例,造成资源的浪费.
其实这个时候AppConfig我们希望它只有一份,就可以使用单例模式.
"""


# -----------单例模式1：使用装饰器--------
def singleton(cls):
    # 单下划线的作用是这个变量只能在当前模块里访问,仅仅是一种提示作用
    # 创建一个字典用来保存类的实例对象
    _instance = {}

    def _singleton(*args, **kwargs):
        # 先判断这个类有没有对象
        if cls not in _instance:
            _instance[cls] = cls(*args, **kwargs)  # 创建一个对象,并保存到字典当中

        # 将实例对象返回
        return _instance[cls]

    return _singleton


@singleton
class A(object):
    a = 1

    def __init__(self, x=0):
        self.x = x
        print('这是A的类的初始化方法')


a1 = A(2)
a2 = A(3)

# 打印对象的唯一id
print(id(a1), id(a2))


# -----------单例模式2：使用类定义私有属性来返回单例--------
class Singleton(object):
    def __init__(self, *args, **kwargs):
        pass

    @classmethod
    def get_instance(cls, *args, **kwargs):
        # 利用反射,看看这个类有没有_instance属性
        if not hasattr(Singleton, '_instance'):
            Singleton._instance = Singleton(*args, **kwargs)

        return Singleton._instance


s1 = Singleton()  # 使用这种方式创建实例的时候,并不能保证单例
s2 = Singleton.get_instance()  # 只有使用这种方式创建的时候才可以实现单例
s3 = Singleton()
s4 = Singleton.get_instance()

print(id(s1), id(s2), id(s3), id(s4))

# ------------单例模式3：基于__new__方法实现的单例模式(推荐使用, 方便)----------
"""
知识点:
<1>  一个对象的实例化过程是先执行类的__new__方法，如果我们没有写,默认会调用object的__new__方法,返回一个实例化对象,
     然后再调用__init__方法,对这个对象进行初始化,我们可以根据这个实现单例.
<2>  在一个类的__new__方法中先判断是不是存在实例,如果存在实例,就直接返回,如果不存在实例就创建.
"""

import threading


class Singleton(object):

    # 线程锁
    _instance_lock = threading.Lock()

    def __init__(self, *args, **kwargs):
        pass

    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            with Singleton._instance_lock:
                if not hasattr(cls, '_instance'):
                    Singleton._instance = super().__new__(cls)

            return Singleton._instance


obj1 = Singleton()
obj2 = Singleton()
print(obj1, obj2)


def task(arg):
    obj = Singleton()
    print(obj)


for i in range(10):
    t = threading.Thread(target=task, args=[i, ])
    t.start()
